<?php
// +----------------------------------------------------------------------
// | QSCMS
// +----------------------------------------------------------------------
// | Copyright (c)  2025 https://www.qqss.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed QSCMS is not free software, commercial use must purchase official license from official website to avoid unnecessary legal disputes.
// +----------------------------------------------------------------------
// | Author: Contract 990504246@qq.com
// +----------------------------------------------------------------------

namespace app\adminapi\controller;

use app\common\model\GoodsOrder;
use app\common\model\Channel;
use app\common\model\User;
use app\common\model\Goods;
use app\common\model\UserViewLog;

class Workbench extends Base
{
    /**
     * Workbench basic data display
     * @auth false
     */
    public function basic()
    {
        $data          = [];
        $data['price'] = array_merge(['title' => trans('today_sales', [], 'workbench')], $this->calculateData(new GoodsOrder(), 'sum', 'total_price'));
        $data['order'] = array_merge(['title' => trans('today_orders', [], 'workbench')], $this->calculateData(new GoodsOrder(), 'count', null));
        $data['user']  = array_merge(['title' => trans('today_users', [], 'workbench')], $this->calculateData(new User(), 'count', null));

        $this->success('', $data);
    }

    private function calculateData($model, $method, $field = null)
    {

        $today     = $model::whereTime('create_time', 'today')->where('status', 1);
        $yesterday = $model::whereTime('create_time', 'yesterday')->where('status', 1);
        $total     = $model::where('status', 1);


        $today     = $field ? $today->$method($field) : $today->$method();
        $yesterday = $field ? $yesterday->$method($field) : $yesterday->$method();
        $total     = $field ? $total->$method($field) : $total->$method();

        if ($today != 0 && $yesterday == 0) {
            $ratio = 100;
        } elseif ($today == 0 && $yesterday != 0) {
            $ratio = -100;
        } elseif ($today == 0 && $yesterday == 0) {
            $ratio = 0;
        } else {
            $ratio = round(($today - $yesterday) / $yesterday, 4) * 100;
        }

        return [
            'today'     => $today,
            'yesterday' => $yesterday,
            'total'     => $total,
            'ratio'     => $ratio
        ];
    }

    /**
     * Payment gateway data
     * @auth false
     */
    public function channelCollectionOrderPriceSum()
    {
        $channels = Channel::where(['status' => 1])
            ->withSum('activeOrders', 'total_price')
            ->select();

        $data = [];
        foreach (collect($channels) as $key => $channel) {
            $data[$key]['value'] = $channel->active_orders_sum ?? 0;
            $data[$key]['name']  = $channel->title;
        }
        $this->success('', $data);
    }

    /**
     * Order payment statistics data
     * @input period Period duration
     * @input unit Unit: day week month year
     * @auth false
     */
    public function orderStatisData()
    {
        $period = inputs('period', 7);
        $unit   = inputs('unit', 'day');
        $data   = [];
        for ($i = 0; $i < $period; $i++) {
            if ($unit === 'month') {
                $start = (new \DateTime("first day of -$i month"))->format('Y-m-d');
                $end   = (new \DateTime("last day of -$i month"))->format('Y-m-d');
            } elseif ($unit === 'week') {
                $start = (new \DateTime("-$i week monday"))->format('Y-m-d');
                $end   = (new \DateTime("-$i week sunday"))->format('Y-m-d');
            } elseif ($unit === 'year') {
                $start = (new \DateTime("first day of january -$i year"))->format('Y-m-d');
                $end   = (new \DateTime("last day of december -$i year"))->format('Y-m-d');
            } else {
                $start = (new \DateTime("-$i day"))->format('Y-m-d');
                $end   = $start;
            }
            $dateFormat = $unit === 'month' ? 'Y-m' : ($unit === 'year' ? 'Y' : 'Y-m-d');
            $data[]     = [
                $unit => date($dateFormat, strtotime($start)),
            ];
        }
        $data = array_reverse($data); // Reverse array, make it sorted by time.
        // data to echarts data format
        $result = [
            'xAxis'  => array_column($data, $unit),
            'series' => array_column($data, 'value')
        ];
        $this->success('', $result);
    }

    /**
     * Software system version information
     * @auth false
     */
    public function systemVersion()
    {
        $data = [
            'authorize' => [
                'status'   => true,
                'end_time' => 0
            ],
            'version'   => config('site.system.version'),
            'author'    => [
                'name'  => config('site.system.author'),
                'email' => config('site.system.email')
            ],
            'license'   => [
                [
                    'name' => trans('official_website'),
                    'url'  => 'https://www.qqss.net',
                ],
                [
                    'name' => trans('software_manual'),
                    'url'  => 'https://www.qqss.net/manual/qscms',
                ],
            ],
        ];
        $this->success('', $data);
    }

    /**
     * Display software license agreement
     * @auth false
     */
    public function license()
    {
        $licenseContent = file_get_contents(base_path() . '/LICENSE.md');
        $this->success('', ['content' => $licenseContent]);
    }

    /**
     * Get sales data
     * @auth false
     */
    public function salesData()
    {
        $days = inputs('days', 7);
        $data = [
            'dates'  => [],
            'sales'  => [],
            'orders' => []
        ];

        for ($i = $days - 1; $i >= 0; $i--) {
            $date            = date('Y-m-d', strtotime("-{$i} days"));
            $data['dates'][] = date('m-d', strtotime($date));

            // Get daily sales - use correct date range query
            $startTime = $date . ' 00:00:00';
            $endTime   = $date . ' 23:59:59';

            $salesAmount     = GoodsOrder::where('create_time', '>=', $startTime)
                ->where('create_time', '<=', $endTime)
                ->where('status', 1)
                ->sum('total_price');
            $data['sales'][] = (float) ($salesAmount ?: 0);

            // Get daily order count
            $orderCount       = GoodsOrder::where('create_time', '>=', $startTime)
                ->where('create_time', '<=', $endTime)
                ->where('status', 1)
                ->count();
            $data['orders'][] = (int) $orderCount;
        }

        $this->success('', $data);
    }

    /**
     * Get user analysis data
     * @auth false
     */
    public function userAnalysisData()
    {
        $data = [
            [
                'name'  => trans('common_user'),
                'value' => User::where('status', 1)->where('level_id', 0)->count()
            ],
            [
                'name'  => trans('vip_user'),
                'value' => User::where('status', 1)->where('level_id', '>', 0)->count()
            ],
            [
                'name'  => trans('today_new_user'),
                'value' => User::whereTime('create_time', 'today')->where('status', 1)->count()
            ],
            [
                'name'  => trans('week_new_user'),
                'value' => User::whereTime('create_time', 'week')->where('status', 1)->count()
            ]
        ];

        $this->success('', $data);
    }

    /**
     * Get product sales ranking data
     * @auth false
     */
    public function categorySalesData()
    {
        $data = [
            'categories' => [],
            'sales'      => []
        ];

        // Get the product with the highest sales volume
        $goods = Goods::where('status', 1)
            ->order('sales_volume', 'desc')
            ->limit(10)
            ->select();

        $count = 0;
        foreach (collect($goods) as $item) {
            // Extract product name, avoid too long
            $name                 = mb_strlen($item->name) > 20 ? mb_substr($item->name, 0, 20) . '...' : $item->name;
            $data['categories'][] = $name;
            $data['sales'][]      = (int) $item->sales_volume;
            $count++;
        }

        // If there are less than 10, add empty data
        while ($count < 10) {
            $data['categories'][] = trans('no_product') . ' #' . $count;
            $data['sales'][]      = 0;
            $count++;
        }

        $this->success('', $data);
    }

    /**
     * Get real-time data (10 data points)
     * @auth false
     */
    public function realtimeData()
    {
        $data = [
            'times'    => [],
            'visitors' => [],
            'orders'   => []
        ];

        // Generate data for the last 10 time points, each point is 2.4 hours apart
        for ($i = 9; $i >= 0; $i--) {
            $currentTime     = time() - ($i * 8640); // Current time minus i*2.4 hours (8640 seconds)
            $timeLabel       = date('H:i', $currentTime);
            $data['times'][] = $timeLabel;

            // Calculate 2.4 hour time period
            $startTime = date('Y-m-d H:i:00', $currentTime);
            $endTime   = date('Y-m-d H:i:59', strtotime($startTime . ' +2 hours 24 minutes'));

            // Get the number of orders in this time period
            $orderCount = GoodsOrder::where('create_time', '>=', $startTime)
                ->where('create_time', '<=', $endTime)
                ->where('status', 1)
                ->count();

            // Get the number of visitors in this time period (from the user access log table)
            $visitorCount = UserViewLog::where('create_time', '>=', $startTime)
                ->where('create_time', '<=', $endTime)
                ->count('DISTINCT user_id');

            $data['visitors'][] = (int) $visitorCount;
            $data['orders'][]   = (int) $orderCount;
        }

        $this->success('', $data);
    }

    /**
     * Get server resource data
     * @auth false
     */
    public function serverResourceData()
    {
        $data = [
            'cpu'              => $this->getCpuUsage(),
            'memory'           => $this->getMemoryUsage(),
            'disk'             => $this->getDiskUsage(),
            'project_memory'   => $this->getProjectMemoryUsage(),
            'memory_total'     => $this->getMemoryTotal(),
            'disk_total'       => $this->getDiskTotal(),
            'php_memory_limit' => $this->getPhpMemoryLimit()
        ];

        $this->success('', $data);
    }

    /**
     * Get CPU usage rate
     */
    private function getCpuUsage()
    {
        if (PHP_OS_FAMILY === 'Linux') {
            // 方法1：使用/proc/stat计算瞬时CPU使用率
            $stat = file_get_contents('/proc/stat');
            if (preg_match('/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/', $stat, $matches)) {
                $user    = (int) $matches[1];
                $nice    = (int) $matches[2];
                $system  = (int) $matches[3];
                $idle    = (int) $matches[4];
                $iowait  = (int) $matches[5];
                $irq     = (int) $matches[6];
                $softirq = (int) $matches[7];

                $total = $user + $nice + $system + $idle + $iowait + $irq + $softirq;
                $used  = $total - $idle;

                if ($total > 0) {
                    return round(($used / $total) * 100, 2);
                }
            }

            // 方法2：使用top命令获取CPU使用率（解析idle值）
            $cmd = "top -bn1 | grep 'Cpu(s)' | awk -F',' '{print \$4}' | awk '{print 100-\$1}'";
            $cpu = shell_exec($cmd);
            if ($cpu !== null && is_numeric(trim($cpu))) {
                return round((float) trim($cpu), 2);
            }

            // 方法3：使用系统负载作为备选
            if (function_exists('sys_getloadavg')) {
                $load      = sys_getloadavg();
                $cpu_count = $this->getCpuCount();
                $cpu_usage = ($load[0] / $cpu_count) * 100;
                return round(min($cpu_usage, 100), 2);
            }
        }

        return 0;
    }



    /**
     * Get CPU core count
     */
    private function getCpuCount()
    {
        if (PHP_OS_FAMILY === 'Linux') {
            $cmd   = "nproc";
            $count = shell_exec($cmd);
            return $count ? (int) $count : 1;
        }
        return 1;
    }

    /**
     * Get memory usage rate
     */
    private function getMemoryUsage()
    {
        if (PHP_OS_FAMILY === 'Linux') {
            $meminfo = file_get_contents('/proc/meminfo');
            preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total);
            preg_match('/MemAvailable:\s+(\d+)/', $meminfo, $available);

            if ($total && $available) {
                $total_kb     = (int) $total[1];
                $available_kb = (int) $available[1];
                $used_kb      = $total_kb - $available_kb;
                return round(($used_kb / $total_kb) * 100, 2);
            }

            // 如果没有MemAvailable，使用传统方法
            preg_match('/MemFree:\s+(\d+)/', $meminfo, $free);
            preg_match('/Buffers:\s+(\d+)/', $meminfo, $buffers);
            preg_match('/Cached:\s+(\d+)/', $meminfo, $cached);

            if ($total && $free) {
                $total_kb   = (int) $total[1];
                $free_kb    = (int) $free[1];
                $buffers_kb = isset($buffers[1]) ? (int) $buffers[1] : 0;
                $cached_kb  = isset($cached[1]) ? (int) $cached[1] : 0;

                // 实际可用内存 = Free + Buffers + Cached
                $actual_available = $free_kb + $buffers_kb + $cached_kb;
                $used_kb          = $total_kb - $actual_available;
                return round(($used_kb / $total_kb) * 100, 2);
            }
        }

        return 0;
    }

    /**
     * Get disk usage rate
     */
    private function getDiskUsage()
    {
        $path = '/';
        if (PHP_OS_FAMILY === 'Windows') {
            $path = 'C:';
        }

        $total = disk_total_space($path);
        $free  = disk_free_space($path);

        if ($total && $free) {
            $used = $total - $free;
            return round(($used / $total) * 100, 2);
        }

        return 0;
    }

    /**
     * Get current project memory usage
     */
    private function getProjectMemoryUsage()
    {
        $current_usage = memory_get_usage(true);
        $limit         = ini_get('memory_limit');

        if ($limit && $limit !== '-1') {
            $limit_bytes = $this->convertToBytes($limit);
            return round(($current_usage / $limit_bytes) * 100, 2);
        }

        // 如果没有设置内存限制，使用系统总内存作为基准
        if (PHP_OS_FAMILY === 'Linux') {
            $meminfo = file_get_contents('/proc/meminfo');
            preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total);
            if ($total) {
                $total_bytes = (int) $total[1] * 1024; // 转换为字节
                return round(($current_usage / $total_bytes) * 100, 2);
            }
        }

        // 备选方案：返回实际使用的MB数（作为百分比显示会很小）
        return round($current_usage / (1024 * 1024 * 10), 2); // 除以10MB作为基准
    }

    /**
     * Convert memory limit string to bytes
     */
    private function convertToBytes($val)
    {
        $val  = trim($val);
        $last = strtolower($val[strlen($val) - 1]);
        $val  = (int) $val;

        switch ($last) {
            case 'g':
                $val *= 1024;
            case 'm':
                $val *= 1024;
            case 'k':
                $val *= 1024;
        }

        return $val;
    }

    /**
     * Get CPU information
     */
    private function getCpuInfo()
    {
        if (PHP_OS_FAMILY === 'Linux') {
            $cpuinfo = file_get_contents('/proc/cpuinfo');
            preg_match('/model name\s*:\s*(.+)/', $cpuinfo, $matches);
            if ($matches) {
                $cpu_model = trim($matches[1]);
                $cpu_count = $this->getCpuCount();

                // Simplify CPU model display, suitable for display in charts
                if (preg_match('/Intel.*?(\w+\d+\w*)/', $cpu_model, $intel_matches)) {
                    return "{$intel_matches[1]} {$cpu_count} " . trans('core');
                } elseif (preg_match('/AMD.*?(\w+\d+\w*)/', $cpu_model, $amd_matches)) {
                    return "{$amd_matches[1]} {$cpu_count} " . trans('core');
                } else {
                    // 如果无法简化，截取前20个字符
                    $short_model = mb_strlen($cpu_model) > 20 ? mb_substr($cpu_model, 0, 20) . '...' : $cpu_model;
                    return "{$short_model} {$cpu_count} " . trans('core');
                }
            }
        }
        return 'Unknown CPU';
    }

    /**
     * Get total memory information
     */
    private function getMemoryTotal()
    {
        if (PHP_OS_FAMILY === 'Linux') {
            $meminfo = file_get_contents('/proc/meminfo');
            preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total);
            if ($total) {
                $total_gb = round((int) $total[1] / 1024 / 1024, 2);
                return "{$total_gb} GB";
            }
        }
        return 'Unknown';
    }

    /**
     * Get total disk information
     */
    private function getDiskTotal()
    {
        $path = '/';
        if (PHP_OS_FAMILY === 'Windows') {
            $path = 'C:';
        }

        $total = disk_total_space($path);
        if ($total) {
            $total_gb = round($total / (1024 * 1024 * 1024), 2);
            return "{$total_gb} GB";
        }
        return 'Unknown';
    }

    /**
     * Get PHP memory limit
     */
    private function getPhpMemoryLimit()
    {
        $cli_limit = ini_get('memory_limit');

        // Try to get the memory limit in Web mode
        $web_limit    = '';
        $web_ini_path = '/www/server/php/82/etc/php.ini';
        if (file_exists($web_ini_path)) {
            $web_ini_content = file_get_contents($web_ini_path);
            if (preg_match('/^memory_limit\s*=\s*(.+)$/m', $web_ini_content, $matches)) {
                $web_limit = trim($matches[1]);
            }
        }

        // Return CLI mode limit, and mark the mode
        $result = $cli_limit ?: 'Unlimited';
        if ($web_limit && $web_limit !== $cli_limit) {
            $result .= " (CLI)";
        } else {
            $result .= " (CLI)";
        }

        return $result;
    }

    /**
     * Get PHP running mode
     */
    private function getPhpSapi()
    {
        $sapi = php_sapi_name();
        if ($sapi === 'cli') {
            return 'CLI (Webman)';
        }
        return strtoupper($sapi);
    }

    /**
     * Get system load
     */
    private function getSystemLoad()
    {
        if (function_exists('sys_getloadavg')) {
            $load = sys_getloadavg();
            return sprintf('%.2f %.2f %.2f', $load[0], $load[1], $load[2]);
        }
        return 'Unknown';
    }

    /**
     * Get system running time
     */
    private function getSystemUptime()
    {
        if (PHP_OS_FAMILY === 'Linux') {
            $uptime = file_get_contents('/proc/uptime');
            if ($uptime) {
                $seconds = (float) explode(' ', trim($uptime))[0];
                $days    = (int) floor($seconds / 86400);
                $hours   = (int) floor(($seconds % 86400) / 3600);
                $minutes = (int) floor(($seconds % 3600) / 60);
                return "{$days} " . trans('day') . " {$hours} " . trans('hour') . " {$minutes} " . trans('minute');
            }
        }
        return 'Unknown';
    }

    /**
     * Get PHP version
     */
    private function getPhpVersion()
    {
        return PHP_VERSION;
    }

}